<?php

class ManifestEntry {
	public $section = '';
	public $line = '';
	public $file_size = 0;
	public $fpath = '';
	public $path = '';
}

class ManifestManager {
	private $entries = array();
	public $manifest_size = 0;
	public $manifest_path = '';
	public $cache_total_size = 0;
	public $cache_file_count = 0;
	private $manifest_fpath = '';
	private $orig_text = '';
	private $mod_text = '';
	public $cache_id = '';
    public function __get($name){
        $fn_name = 'get_' . $name;
        if (method_exists($this, $fn_name)){
            return $this->$fn_name();
        } else {
            return null;
        }
    }
    public function __set($name, $value){
        $fn_name = 'set_' . $name;
        if (method_exists($this, $fn_name)){
            $this->$fn_name($value);
        } else {
        	$stop = '';
        }
    }
    public function __construct($fname){
    	$docroot = rtrim(getenv('DOCUMENT_ROOT'),'/');
		$path_parts = pathinfo($_SERVER['SCRIPT_FILENAME']);
		$rel_path = rtrim($path_parts['dirname'],'/');
        if (substr($fname,0,1) == '/') {
			$fpath = $docroot . $fname;
    	} else if(strpos($fname,'://')) {
    		$fpath = $fname;
    	} else if(strpos($fname,':/') === 1) {
    		$fpath = $fname;
    	} else {
			$fpath = $rel_path . '/' . $fname;
    	}
    	$this->manifest_path = $fname;
    	$this->manifest_fpath = $fpath;
    	$this->manifest_size = filesize($fpath);
    	$lines = file($fpath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    	$cur_section = 'CACHE';
    	foreach ($lines as $i => $line) {
    		$line = trim($line);
    		$added = false;
   			// Make sure first line is CACHE MANIFEST
    		if ($i == 0 && $line != 'CACHE MANIFEST') {
    			return false;
    		} else if ($i == 0 && $line == 'CACHE MANIFEST') {
				// Valid manifest file
    		} else if ($line == 'CACHE:') {
				$cur_section = 'CACHE';
    		} else if ($line == 'FALLBACK:') {
				$cur_section = 'FALLBACK';
    		} else if ($line == 'NETWORK:') {
				$cur_section = 'NETWORK';
    		} else if (substr($line,0,1) == '#' || $line == '') {
    			// Comment/Blank line
				$file = new ManifestEntry();
				$file->section = $cur_section;
				$file->line = $line;
				$this->entries[] = $file;
    		} else {
    			// Section listing
    			if ($cur_section == "CACHE"){
    				$added = true;
	    			if (substr($line,0,1) == '/') {
						$fpath = $docroot . $line;
	    			} else if(strpos($line,'://')) {
	    				$fpath = $line;
	    			} else {
						$fpath = $rel_path . '/' . $line;
	    			}
	    			if (is_dir($fpath)) {
						$this->addCacheFolder($line);
	    			} else {
	    				$this->addCacheFile($line);
	    			}
    			} else {
					$file = new ManifestEntry();
					$file->section = $cur_section;
					$file->line = $line;
					$this->entries[] = $file;
    			}
    		}
		}
		$this->cache_file_count = $this->cacheFileCount();
		$this->cache_total_size = $this->cacheTotalSize();
		// LoadCacheFile complete
		$days = 30;
	    if (isset($_COOKIE['MANIFEST'])) {
			$man = $_COOKIE['MANIFEST'];
			setcookie("MANIFEST", '', time() - ($days*24*60*60));
		} else {
			$man = '';
		}
		if (!isset($_COOKIE['MANIFEST_ID'])) {
			//$man_id = rand_str();
			$man_id = -1;
			setcookie("MANIFEST_ID", $man_id, time() + ($days*24*60*60));
		} else {
			$man_id = $_COOKIE['MANIFEST_ID'];
		}
		if ($man == 'refresh'){
			$man_id = rand_str();
			setcookie("MANIFEST_ID", $man_id, time() + ($days*24*60*60));
		} else if ($man == 'clear'){
			$man_id = '-1';
			setcookie("MANIFEST_ID", $man_id, time() + ($days*24*60*60));
		} else if ($man == 'net'){
			$man_id = '-1';
			setcookie("MANIFEST_ID", $man_id, time() + ($days*24*60*60));
		}
		$this->cache_id = $man_id;
		if ($man_id == '-1' && $man == 'clear') {
			$this->sendNotFound();
		} else if($man_id == '-1') {
			$this->sendNetworkOnly();
		} else {
			$this->sendCache();
		}
    }
    public function sendCache(){
    	$days = 30;
		setcookie("MANIFEST_SIZE", $this->manifest_size, time() + ($days*24*60*60));
		setcookie("MANIFEST_PATH", $this->manifest_path, time() + ($days*24*60*60));
		setcookie("MANIFEST_CACHE_TOTAL_SIZE", $this->cache_total_size, time() + ($days*24*60*60));
		setcookie("MANIFEST_CACHE_FILE_COUNT", $this->cache_file_count, time() + ($days*24*60*60));
    	$this->setHeader();
		echo "CACHE MANIFEST\n";
		echo "# " . $this->cacheTotalSize() . " bytes in " . $this->cacheFileCount() . "\n";
		if ($this->cache_id != ''){
			echo "# " . $this->cache_id . "\n";
		}
		$cur_section = "CACHE";
		foreach ($this->entries as $i => $entry) {
			if ($entry->section != $cur_section) {
				echo "$entry->section:\n";
			}
			echo $entry->line . "\n";
		}
		return $tot;
    }
    public function sendNetworkOnly(){
    	$days = 30;
		setcookie("MANIFEST_SIZE", 0, time() + ($days*24*60*60));
		setcookie("MANIFEST_PATH", $this->manifest_path, time() + ($days*24*60*60));
		setcookie("MANIFEST_CACHE_TOTAL_SIZE", 0, time() + ($days*24*60*60));
		setcookie("MANIFEST_CACHE_FILE_COUNT", 0, time() + ($days*24*60*60));
    	$this->setHeader();
		echo "CACHE MANIFEST\n";
		echo "NETWORK:\n";
		echo "*\n";
    }
	public function sendNotFound(){
		$days = 30;
		setcookie("MANIFEST_SIZE", 0, time() + ($days*24*60*60));
		setcookie("MANIFEST_PATH", $this->manifest_path, time() + ($days*24*60*60));
		setcookie("MANIFEST_CACHE_TOTAL_SIZE", 0, time() + ($days*24*60*60));
		setcookie("MANIFEST_CACHE_FILE_COUNT", 0, time() + ($days*24*60*60));
		header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
		header("Status: 404 Not Found");
		$_SERVER['REDIRECT_STATUS'] = 404;
	}
	public function addCacheFile($fname){
    	$docroot = rtrim(getenv('DOCUMENT_ROOT'),'/');
		$path_parts = pathinfo($_SERVER['SCRIPT_FILENAME']);
		$rel_path = rtrim($path_parts['dirname'],'/');
    	if (substr($fname,0,1) == '/') {
    		// full path
			$fpath = $docroot . $fname;
    	} else if(strpos($fname,'://')) {
    		$fpath = $fname;
    	} else {
    		// rel path
			$fpath = $rel_path . '/' . $fname;
    	}
		if (file_exists($fpath)) {
			$size = filesize($fpath);
			$file = new ManifestEntry();
			$file->path = $fname;
			$file->line = $fname;
			$file->fpath = $fpath;
			$file->file_size = $size;
			$file->section = "CACHE";
			$this->entries[] = $file;
			return true;
		}
		return false;
	}
	public function hasCacheFile($fname){
		foreach ($this->entries as $i => $entry) {
			if ($entry->section == "CACHE") {
				if ( $entry->path == $fname){
					return true;
				}
			}
		}
		return false;
	}
	public function cacheTotalSize(){
		$tot = 0;
		foreach ($this->entries as $i => $entry) {
			if ($entry->section == "CACHE") {
				$tot = $tot + $entry->file_size;
			}
		}
		return $tot;
	}
	public function cacheFileCount(){
		$tot = 0;
		foreach ($this->entries as $i => $entry) {
			if ($entry->section == "CACHE" && $entry->fpath != '') {
				$tot++;
			}
		}
		return $tot;
	}
	private function setHeader(){
		header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
		header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
		header("Content-type: text/cache-manifest");
	}
	private function addCacheFolder($webPath, $includeSubs = true) {
		$webPath = rtrim($webPath, '/');
		$folders = array($webPath);
		$files = array();
		for($i=0;$i<count($folders);$i++) {
			$curpath = $folders[$i];
			$folders = array_merge($folders, $this->getDirContents($curpath,false,true));
			$files = array_merge($files, $this->getDirContents($curpath,true,false));
			if (!$includeSubs) {
				$i = count($folders);
			}
		}
		for($i=0;$i<count($files);$i++) {
			$this->addCacheFile($files[$i]);
		}
	}
	private function getDirContents($webPath, $getFiles = true, $getFolders = true){
		$webPath = rtrim($webPath, '/');
		$docroot = rtrim(getenv('DOCUMENT_ROOT'),'/');
		$dirPath = $docroot . '/' . ltrim($webPath,'/');
		$res = array();
		if ($handle = opendir($dirPath)) {
		   // keep reading the directory entries 'til the end
		   while (false !== ($file = readdir($handle))) {
		      // just skip the reference to current and parent directory
		      if ($file != "." && $file != "..") {
		      	if ((substr("$dirArray[$index]", 0, 1) != ".") && (substr("$dirArray[$index]", 0, 1) != "_")) {
					if ((is_dir("$dirPath/$file") && $getFolders)) {
		            	$res[] = "$webPath/$file";
					} else if ($getFiles) {
						if ($this->fileTypeCheck($file)){
							$res[] = "$webPath/$file";
						}
					}
		         }
		      }
		   }
		   closedir($handle);
		}
		return $res;
	}
	private function fileTypeCheck($file){
		$fileTypes = array('js','css','jpg','jpeg','png','gif');
		for ($i=0;$i<count($fileTypes);$i++){
			$typ = '.' . $fileTypes[$i];
			if (strlen($file) > strlen($typ)){
				if (substr($file,strlen($file) - strlen($typ)) == $typ){
					return true;
				}
			}
		}
		return false;
	}
}
function rand_str($strsize = 24) {
	srand(time());
	$ret = '';
	for($i=1;$i<=$strsize;$i++) {
		#$char = chr(rand(65, 90));
		$char = chr(rand(97, 122));
		$ret .= $char;
	}
	return $ret;
}
?>